# Universal shell
import gc, os, json, sys

from DeflateIO.lzw2 import LZW

class UserManager:
    def __new__(cls):
        if not hasattr(cls, 'instance'):
            cls.instance = super(UserManager, cls).__new__(cls)
        return cls.instance

    users = {
        "admin": hash("1234")
    }

    def __init__(self) -> None:
        try:
            with open('/users.json') as file:
                self.users = json.loads(file.read())
        except: self.UpdateDB()

    def UpdateDB(self) -> None:
        with open('/users.json', 'w') as f:
            f.write(json.dumps(self.users))

    def CreateUser(self, login :str, password :str) -> None:
        self.users[login] = hash(password)
        self.UpdateDB()

    def GetPasswordHash(self, login :str) -> str:
        return self.users.get(login)

    def Login(self, login :str, password :str) -> bool:
        if login in self.users:
            if hash(password) == self.GetPasswordHash(login):
                return True
        return False

class BinaryExecution:
    def Run(file):
        with open(file, 'rb') as file:
            pass

class BaseCommand:

    name :str = "" # Command name
    permission :str = "-" # Permission. Set name of permission, or set * for admin only, and - for any user

    def Execute() -> bool:
        return False

class Shell:
    user = 'admin'

    def Execute(self, cmd):
        if cmd[:2] == "::":
            print(eval(cmd[2:]))
            return

        if cmd == "whoami":
            print(self.user)
            return

        if cmd == "free":
            print(f'Free mem: \n- {gc.mem_free()} B\n- {gc.mem_free() // 1024} KB')
            return

        if cmd == "about":
            print('QubicOS 3 with Rubik3 kernel, Build #62')
            return

        if cmd == "clear":
            print(chr(27) + "[2J")  # clear terminal
            print("\x1b[2J\x1b[H")  # cursor up
            return

        if cmd == "ps":
            execfile(BASE_KERNEL_PATH + 'apps/ps.py')
            return

        if cmd[:4] == "tone":
            execfile(BASE_KERNEL_PATH + 'apps/tone.py', {'arg': cmd})
            return

        if cmd[:2] == "ls":
            if ' ' in cmd:
                oldcwd = os.getcwd()
                os.chdir(cmd.split(' ')[1])
                files = os.ilistdir()
            else:
                files = os.ilistdir()
                files2 = []

                for file in files:
                    if file[1] == 16384:
                        print(f'\x1b[34m{file[0]}\x1b[0m')
                    else:
                        def genSize(size):
                            if size == 0:
                                return ''
                            if size < 1024:
                                return f": ~{size} B"
                            elif size < 1024 * 1024:
                                return f": ~{size // 1024} \x1b[36mKB\x1b[0m"
                            elif size < 1024 * 1024 * 1024:
                                return f": ~{size // 1024 // 1024} \x1b[34mMB\x1b[0m"
                            elif size < 1024 * 1024 * 1024 * 1024:
                                return f": ~{size // 1024 // 1024 // 1024} \x1b[35mGB\x1b[0m"
                            elif size < 1024 * 1024 * 1024 * 1024 * 1024:
                                return f": ~{size // 1024 // 1024 // 1024 // 1024} \x1b[35mTB\x1b[0m"

                        files2.append(f'{file[0]} {genSize(file[3])}')
                for file in files2: print(file)
            return

        if cmd[:2] == "cd" and ' ' in cmd:
            d = cmd.split(' ', 1)[1]
            try:
                os.chdir(d)
            except Exception as e:
                print(f'Cannot change directory! {e}')
            return

        if cmd[:5] == "touch" and ' ' in cmd:
            name = cmd.split(' ', 1)[1]
            with open(name, 'w'): print(f"Created {name}")
            return

        if cmd[:3] == "put" and len(cmd.split(' ')) >= 3:

            name = cmd.split(' ')[1]
            data = cmd.split(' ', 2)[2]
            with open(name, 'a') as file:
                file.write(data)
            return

        if cmd[:3] == "cat" and ' ' in cmd:
            try:
                with open(cmd.split(' ', 1)[1]) as file:
                    while True:
                        # Читаем по 1024 символа за раз
                        chunk = file.read(1024)
                        if not chunk:
                            # Если больше не осталось символов для чтения, выходим из цикла
                            break
                        print(chunk, end='')
                print('')

            except Exception as e:
                print(f'Cannot cat file! {e}')
            return

        if cmd == "di":
            from os import statvfs
            flash_free = int(statvfs("/")[0]) * int(statvfs("/")[3])
            print("Flash Free: " + str(flash_free))
            return

        if cmd[:2] == "di" and len(cmd.split(' ', 1)) == 2:
            path = cmd.split(' ', 1)[1]
            from os import statvfs
            flash_free = int(statvfs(path)[0]) * int(statvfs(path)[3])
            print(f"Free bytes at {path}: " + str(flash_free))
            return
            
        if cmd[:5] == "mkdir" and len(cmd.split(' ', 1)) == 2:
            os.mkdir(cmd.split(' ', 1)[1])

        if cmd[:2] == "./":
            BinaryExecution.Run(cmd[2:])
            return

        if cmd[:5] == "qedit" and ' ' in cmd:
            execfile(BASE_KERNEL_PATH + 'apps/qedit.py', {'file': cmd.split(' ', 1)[1]})
            return

        if cmd in ['qbasic', 'qbas', 'basic']:
            execfile(BASE_KERNEL_PATH + 'apps/QuickBasic.py', {})
            #ContainerPool().CreateContainer("qedit", lambda: execfile(BASE_KERNEL_PATH + "apps/QuickBasic.py", {"syscall": Pipe, "BASE_KERNEL_PATH": BASE_KERNEL_PATH}))
            #ContainerPool().GetContainer("qedit").start([])
            return
        if cmd.split(' ')[0] in ['edit', 'me']:
            from packages.me.pye import pye
            pye(*cmd.split(' ')[1:])
            return
        if cmd.split(' ', 1)[0] in os.listdir(BASE_KERNEL_PATH + "packages"):
            file = cmd.split(' ', 1)[0]
            execfile(BASE_KERNEL_PATH + f'packages/{file}/{file}.py', {"cmd": cmd, "BASE_KERNEL_PATH": BASE_KERNEL_PATH})
            return

    def Login(self):
        while True:
            print("")
            login = input("Login as: ")
            password = input(f"{login} password: ")
            if UserManager().Login(login, password):
                self.user = login
                self.Run()
                break
            print("Invalid credinitials")

    def Run(self):
        while True:
            user_input = input(f'[ {os.getcwd()} @ {self.user} ]# ')
            self.Execute(user_input)

Shell().Login()
